#include "config.h"

#include <gtest/gtest.h>

#include <memory>
#include <string>

using namespace cfg;

// 针对对象键合并与标量覆盖的基本验证
TEST(MergeTest, MergeObject_Basic)
{
    Builder b;
    // dst 初始
    b.set("a", int64_t{1})
        .set("b.x", int64_t{2})
        .set("b.y", std::string("foo"))
        .set("c", std::string("bar"));

    // src 合并：覆盖 c，扩展 b，新增 d
    Object src_b;
    src_b.props["y"] = std::string("baz");
    src_b.props["z"] = int64_t{9};

    Object src_obj;
    src_obj.props["b"] = std::shared_ptr<const Object>(std::make_shared<Object>(src_b));
    src_obj.props["c"] = int64_t{7};
    src_obj.props["d"] = std::string("new");

    b.merge_object(src_obj);

    auto fr = b.freeze();
    ASSERT_TRUE(fr.has_value());
    View v(*fr);

    // a 保持
    auto a = v.lookup("a");
    ASSERT_TRUE(a.has_value());
    auto ai = a->get_int64();
    ASSERT_TRUE(ai.has_value());
    EXPECT_EQ(*ai, 1);

    // b.y 覆盖为 baz，b.z 新增
    auto by = v.lookup("b.y");
    ASSERT_TRUE(by.has_value());
    auto bys = by->get_string();
    ASSERT_TRUE(bys.has_value());
    EXPECT_EQ(*bys, "baz");

    auto bz = v.lookup("b.z");
    ASSERT_TRUE(bz.has_value());
    auto bzi = bz->get_int64();
    ASSERT_TRUE(bzi.has_value());
    EXPECT_EQ(*bzi, 9);

    // c 被覆盖为 7
    auto c = v.lookup("c");
    ASSERT_TRUE(c.has_value());
    auto ci = c->get_int64();
    ASSERT_TRUE(ci.has_value());
    EXPECT_EQ(*ci, 7);

    // d 存在且为字符串
    auto d = v.lookup("d");
    ASSERT_TRUE(d.has_value());
    auto ds = d->get_string();
    ASSERT_TRUE(ds.has_value());
    EXPECT_EQ(*ds, "new");
}

// 类型不匹配下的覆盖行为：数组/对象/标量
TEST(MergeTest, MergeObject_TypeMismatchOverwrite)
{
    Builder b;
    b.set("x", int64_t{1})
        .set("y[0]", int64_t{1})
        .set("y[1]", int64_t{2})
        .set("z.k", std::string("v"));

    // 使用 merge_object 覆盖成不同类型
    Object src_obj;

    // x: 标量 -> 数组 [9]
    auto arr_x = std::make_shared<Array>();
    arr_x->items.push_back(int64_t{9});
    src_obj.props["x"] = std::shared_ptr<const Array>(arr_x);

    // y: 数组 -> 标量 "str"
    src_obj.props["y"] = std::string("str");

    // z: 对象 -> 标量 42
    src_obj.props["z"] = int64_t{42};

    b.merge_object(src_obj);

    auto fr = b.freeze();
    ASSERT_TRUE(fr.has_value());
    View v(*fr);

    auto x = v.lookup("x[0]");
    ASSERT_TRUE(x.has_value());
    auto xi = x->get_int64();
    ASSERT_TRUE(xi.has_value());
    EXPECT_EQ(*xi, 9);

    auto y = v.lookup("y");
    ASSERT_TRUE(y.has_value());
    auto ys = y->get_string();
    ASSERT_TRUE(ys.has_value());
    EXPECT_EQ(*ys, "str");

    auto z = v.lookup("z");
    ASSERT_TRUE(z.has_value());
    auto zi = z->get_int64();
    ASSERT_TRUE(zi.has_value());
    EXPECT_EQ(*zi, 42);
}

// 合并与冲突策略：深合并与浅合并的行为验证
// 该用例构造基础对象树，然后分别以不同策略进行合并，检查预期结果。
TEST(MergeTest, MergeStrategiesAndConflictPolicies)
{
    // 构造源对象：db: { host: "new", opts: { timeout: 30 } }
    Object src_db_obj;
    src_db_obj.props["host"] = std::string("new");
    Object src_opts_obj;
    src_opts_obj.props["timeout"] = int64_t(30);
    src_db_obj.props["opts"] =
        std::shared_ptr<const Object>(std::make_shared<Object>(src_opts_obj));

    Object src_obj;
    src_obj.props["db"] = std::shared_ptr<const Object>(std::make_shared<Object>(src_db_obj));

    // 1) 深合并 + 覆盖：期望保留已有 pool，覆盖 host，新增 timeout
    {
        Builder b;
        b.set("db.host", std::string("old"));
        b.set("db.opts.pool", int64_t(10));
        b.set_merge_strategy(MergeStrategy::Deep).set_conflict_policy(ConflictPolicy::Overwrite);
        b.merge_object(src_obj);
        auto fr = b.freeze();
        ASSERT_TRUE(fr.has_value());

        View v(*fr);
        auto host = v.lookup("db.host");
        ASSERT_TRUE(host.has_value());
        auto hs = host->get_string();
        ASSERT_TRUE(hs.has_value());
        EXPECT_EQ(*hs, "new");

        auto pool = v.lookup("db.opts.pool");
        ASSERT_TRUE(pool.has_value());
        auto pi = pool->get_int64();
        ASSERT_TRUE(pi.has_value());
        EXPECT_EQ(*pi, 10);

        auto timeout = v.lookup("db.opts.timeout");
        ASSERT_TRUE(timeout.has_value());
        auto ti = timeout->get_int64();
        ASSERT_TRUE(ti.has_value());
        EXPECT_EQ(*ti, 30);
    }

    // 2) 浅合并 + 覆盖：期望整个 db 被覆盖，原有 pool 消失
    {
        Builder b;
        b.set("db.host", std::string("old"));
        b.set("db.opts.pool", int64_t(10));
        b.set_merge_strategy(MergeStrategy::Shallow).set_conflict_policy(ConflictPolicy::Overwrite);
        b.merge_object(src_obj);
        auto fr = b.freeze();
        ASSERT_TRUE(fr.has_value());

        View v(*fr);
        auto host = v.lookup("db.host");
        ASSERT_TRUE(host.has_value());
        auto hs = host->get_string();
        ASSERT_TRUE(hs.has_value());
        EXPECT_EQ(*hs, "new");

        // pool 应不存在
        auto pool = v.lookup("db.opts.pool");
        EXPECT_FALSE(pool.has_value());

        // timeout 应存在
        auto timeout = v.lookup("db.opts.timeout");
        ASSERT_TRUE(timeout.has_value());
        auto ti = timeout->get_int64();
        ASSERT_TRUE(ti.has_value());
        EXPECT_EQ(*ti, 30);
    }

    // 3) 冲突策略：Keep - 保留已有值
    {
        Builder b;
        b.set("a", int64_t(1));
        Object s;
        s.props["a"] = int64_t(2);
        b.set_merge_strategy(MergeStrategy::Shallow).set_conflict_policy(ConflictPolicy::Keep);
        b.merge_object(s);
        auto fr = b.freeze();
        ASSERT_TRUE(fr.has_value());
        View v(*fr);
        auto a = v.lookup("a");
        ASSERT_TRUE(a.has_value());
        auto ai = a->get_int64();
        ASSERT_TRUE(ai.has_value());
        EXPECT_EQ(*ai, 1); // 保留旧值
    }

    // 4) 冲突策略：Error - 记录错误且保留旧值
    {
        Builder b;
        b.set("a", int64_t(1));
        Object s;
        s.props["a"] = int64_t(2);
        b.set_merge_strategy(MergeStrategy::Shallow).set_conflict_policy(ConflictPolicy::Error);
        b.merge_object(s);
        auto fr = b.freeze();
        ASSERT_FALSE(fr.has_value());
        const auto & errs = fr.error();
        bool found = false;
        for (const auto & e : errs)
        {
            if (e.message.find("merge conflict") != std::string::npos)
            {
                found = true;
                break;
            }
        }
        EXPECT_TRUE(found);
    }
}